﻿using System;
using System.Collections.Generic;
using System.Text;

namespace SourcePacker
{
    class IncludeLocator
    {
        /// <summary>
        /// 包含文件输出结果
        /// 键：#include开始索引
        /// 值->键：#include对应文件起始索引（包含引号或尖括号）
        /// 值->值：#include对应文件结束索引（越过引号或尖括号）
        /// 必须按照开始索引升序排列！！！！
        /// </summary>
        public Dictionary<int, KeyValuePair<int, int>> Results { get; private set; }

        public IncludeLocator(SourceFile sourceFile)
        {
            Results = new Dictionary<int, KeyValuePair<int, int>>();
            state = State.InLineBreak;

            foreach (char c in sourceFile.Text)
            {
                next(c);
                ++currentIndex;
            }
        }

        enum State
        {
            Free,
            InChar,
            InString,
            InComment,
            AfterStarInComment,
            InLineComment,
            InLineBreak,
            AfterForwardSlash,
            AfterBackSlash,
            AfterBackSlashInChar,
            AfterBackSlashInString,
            AfterSharp,
            AfterInclude,
            AfterAngle
        };

        State state;
        State stateBeforeComment;
        State stateBeforeBackSlash;

        int includeChars = 0;
        int currentIndex = 0;
        int includeFileStart = -1;
        int includeShartStart = -1;

        void next(char c)
        {
            switch (state)
            {
                case State.Free: onStateFree(c); break;
                case State.InChar: onStateInChar(c); break;
                case State.InString: onStateInString(c); break;
                case State.InComment: onStateInComment(c); break;
                case State.InLineBreak: onStateInLineBreak(c); break;
                case State.AfterStarInComment: onStateAfterStarInComment(c); break;
                case State.InLineComment: onStateInLineComment(c); break;
                case State.AfterForwardSlash: onStateAfterForwardSlash(c); break;
                case State.AfterBackSlash: onStateAfterBackSlash(c); break;
                case State.AfterBackSlashInChar: onStateAfterBackSlashInChar(c); break;
                case State.AfterBackSlashInString: onStateAfterBackSlashInString(c); break;
                case State.AfterSharp: onStateAfterSharp(c); break;
                case State.AfterInclude: onStateAfterInclude(c); break;
                case State.AfterAngle: onStateAfterAngle(c); break;
            }
        }

        void onStateFree(char c)
        {
            switch (c)
            {
                case '/': stateBeforeComment = state; state = State.AfterForwardSlash; break;
                case '\\': state = State.AfterBackSlash; break;
                case '\'': state = State.InChar; break;
                case '\"': state = State.InString; break;
                case '\r':
                case '\n': state = State.InLineBreak; break;
                default: state = State.Free; break;
            }
        }

        void onStateInChar(char c)
        {
            switch (c)
            {
                case '\\': state = State.AfterBackSlashInChar; break;
                case '\'': state = State.Free; break;
                default: break;
            }
        }

        void onStateInString(char c)
        {
            switch (c)
            {
                case '\\': state = State.AfterBackSlashInString; break;
                case '\"':
                    if (includeChars == 7 && includeFileStart >= 0)
                        addInclude();

                    state = State.Free; break;
                default: break;
            }
        }

        void onStateInComment(char c)
        {
            switch (c)
            {
                case '*': state = State.AfterStarInComment; break;
                default: break;
            }
        }

        void onStateAfterStarInComment(char c)
        {
            switch (c)
            {
                case '*': state = State.AfterStarInComment; break;
                case '/': state = stateBeforeComment; break;
                default: state = State.InComment; break;
            }
        }

        void onStateInLineComment(char c)
        {
            switch (c)
            {
                case '\n': state = State.InLineBreak; break;
                default: break;
            }
        }

        void onStateInLineBreak(char c)
        {
            switch (c)
            {
                case ' ':
                case '\r':
                case '\t':
                case '\n': break;
                case '#': includeShartStart = currentIndex; state = State.AfterSharp; includeChars = 0; break;
                default: onStateFree(c); break;
            }
        }

        void onStateAfterForwardSlash(char c)
        {
            switch (c)
            {
                case '/': state = State.InLineComment; break;
                case '*': state = State.InComment; break;
                default: onStateFree(c); break;
            }
        }

        void onStateAfterBackSlash(char c)
        {
            switch (c)
            {
                //case '\r': break;
                //case '\n': state = stateBeforeBackSlash; break;
                default: state = stateBeforeBackSlash; break;//State.Free; break;
            }
        }

        void onStateAfterBackSlashInChar(char c)
        {
            switch (c)
            {
                default: state = State.InChar; break;
            }
        }

        void onStateAfterBackSlashInString(char c)
        {
            switch (c)
            {
                default: state = State.InString; break;
            }
        }

        void onStateAfterSharp(char c)
        {
            switch (c)
            {
                case '/': stateBeforeComment = state; state = State.AfterForwardSlash; return;
                case '\\': stateBeforeBackSlash = state; state = State.AfterBackSlash; return;
                case ' ':
                case '\t': return;
                default: break;
            }

            if (c == "include"[includeChars])
            {
                if (++includeChars == 7)
                {
                    state = State.AfterInclude;
                    includeFileStart = -1;
                }
            }
            else
            {
                onStateFree(c);
            }
        }

        void onStateAfterInclude(char c)
        {
            switch (c)
            {
                case '/': stateBeforeComment = state; state = State.AfterForwardSlash; break;
                case '\\': stateBeforeBackSlash = state; state = State.AfterBackSlash; break;
                case ' ':
                case '\t': return;
                case '\"': includeFileStart = currentIndex; state = State.InString; break;
                case '<': includeFileStart = currentIndex; state = State.AfterAngle; break;
                default: break;
            }
        }

        void onStateAfterAngle(char c)
        {
            switch (c)
            {
                case '\\': stateBeforeBackSlash = state; state = State.AfterBackSlash; break;
                case '>': addInclude(); state = State.Free; break;
                default: break;
            }
        }

        void addInclude()
        {
            if (includeFileStart >= 0)
                Results.Add(includeShartStart, new KeyValuePair<int, int>(includeFileStart, currentIndex + 1));

            includeFileStart = -1;
        }
    }
}
